import {
  DatePicker as AntDatePicker,
  TimePicker as AntTimePicker,
  type DatePickerProps,
  type TimePickerProps
} from 'antd';
import type { RangePickerProps } from 'antd/es/date-picker';
import locale from 'antd/es/date-picker/locale/zh_CN';
import toDayJs, { Dayjs } from 'dayjs';
import 'dayjs/locale/zh-cn';
import { compHoc, iup, IUPComponent, type TypeExtends } from '../util';

AntTimePicker.displayName = 'TimePicker';

const pickerFormat = {
  date: 'YYYY-MM-DD',
  year: 'YYYY',
  quarter: 'YYYY-[Q]Q',
  month: 'YYYY-MM',
  week: 'YYYY-wo'
};

function convertToDate(value, format) {
  if (format === pickerFormat.quarter && value.indexOf('-Q') > 0) {
    const parts = value.split('-Q');
    const year = parseInt(parts[0], 10);
    const quarter = parseInt(parts[1], 10);

    // 计算季度的月份
    const month = quarter * 3 - 2;

    // 创建 Day.js 对象
    return toDayJs(new Date(year, month, 1));
  }
  return toDayJs(value, format);
}

const getFormat = (props, defaultFormat, type) => {
  const showTime = props.showTime ?? type === 'datetime';
  return (
    props.format ||
    pickerFormat[props.picker] ||
    (showTime ? 'YYYY-MM-DD ' + (showTime.format || 'HH:mm:ss') : '') ||
    defaultFormat
  );
};

function getDatePicker(Comp, type = '', dFormat = 'YYYY-MM-DD') {
  return class extends IUPComponent {
    constructor(props) {
      super(props);
      const defaultFormat = getFormat(props, dFormat, type);
      this.state = {
        props,
        open: props.defaultOpen || props.open,
        defaultFormat,
        value: this.getDayJs(props.value ?? props.defaultValue, defaultFormat)
      };
    }

    static getDerivedStateFromProps(nextProps, prevState) {
      const { value, format, picker, showTime, open } = nextProps;
      // 当传入的value发生变化的时候，更新state, 这个props可以被当做缓存，仅用作判断
      const newState: any = {};
      if (open !== prevState.props.open) {
        newState.open = open;
        newState.props = nextProps;
      }
      if (value !== prevState.props.value) {
        newState.value = value;
        newState.props = nextProps;
      }
      if (
        format !== prevState.props.format ||
        picker !== prevState.props.picker ||
        showTime !== prevState.props.showTime
      ) {
        newState.defaultFormat = getFormat(nextProps, dFormat, type);
        newState.props = nextProps;
      }
      return newState.props ? newState : null;
    }

    getShowTime() {
      return this.props.showTime ?? type === 'datetime';
    }

    setValue(value, cb?) {
      if (this.state.value === value) {
        return;
      }
      value = this.getDayJs(value);
      if (value && (iup.isArray(value) ? value.some((v) => !v.isValid()) : !value.isValid())) {
        return;
      }

      const newValue = value || null;
      this.setState({ value: newValue }, () => {
        this.props.onChange && this.props.onChange(this.getValue());
        cb && cb(newValue);
      });
    }

    getValue() {
      const value = this.getDayJs(this.state.value);
      if (!value) {
        return null;
      }
      return type === 'range'
        ? value.map((val) => (val ? val.format(this.state.defaultFormat) : null))
        : value.format(this.state.defaultFormat);
    }

    getRawValue() {
      return this.getDayJs(this.state.value || null);
    }

    isOpened() {
      return !!this.state.open;
    }

    onDatePickerChange = (value) => {
      if (this.props.hasOwnProperty('value')) {
        // 外部受控
        this.state.value = this.getDayJs(value);
        this.props.onChange && this.props.onChange(this.getValue());
      } else {
        this.setValue(value);
      }
      this.innerNotify([value], 'onChange').then();
    };

    getDayJs(value: any, defaultFormat?) {
      if (iup.isArray(value)) {
        return value.map((v) => this.getDayJs(v));
      }
      if (iup.isDate(value)) {
        return toDayJs(value);
      }
      return !value || toDayJs.isDayjs(value) ? value : convertToDate(value, defaultFormat || this.state.defaultFormat);
    }

    onOpenChange = (open: boolean) => {
      if (!this.props.hasOwnProperty('open')) {
        this.setState({ open });
      }
      this.props.onOpenChange?.(open);

      // 明明可以单击选择，有人就是要双击，导致事件穿透，触发了弹窗下组件的点击事件，测试觉得这是个bug，临时这样处理
      const targetEl = window.event?.target as any;
      if (!open && !this.getShowTime() && targetEl) {
        if (targetEl?.className?.indexOf?.('ant-picker-') === 0) {
          // 如果是点击单元格
          document.body.style.pointerEvents = 'none';
          setTimeout(() => {
            document.body.style.pointerEvents = '';
          }, 250);
        }
      }
    };

    render() {
      const { value, onChange, onOpenChange, observer, ...dataPickerProps } = this.props as any;
      if (dataPickerProps.disabled) {
        dataPickerProps.allowClear = false;
        dataPickerProps.suffixIcon = null;
        dataPickerProps.placeholder = '';
        dataPickerProps.inputReadOnly = true;
      } else {
        dataPickerProps.allowClear = dataPickerProps.allowClear ?? true;
        if (type === 'range' && iup.isString(dataPickerProps.placeholder)) {
          dataPickerProps.placeholder = [dataPickerProps.placeholder, dataPickerProps.placeholder];
        }
      }
      return (
        <Comp
          ref={this.outRef}
          style={{ width: '100%' }}
          locale={locale}
          {...dataPickerProps}
          open={this.state.open}
          showTime={this.getShowTime()}
          value={this.getDayJs(this.state.value)}
          // format={{
          //   format: this.state.defaultFormat,
          //   type: 'mask'
          // }}
          onOpenChange={this.onOpenChange}
          onChange={this.onDatePickerChange}
        />
      );
    }
  };
}

type TypeProps<T> = TypeExtends<T, { onChange?: (value: any) => void }>;

export const DatePicker = compHoc<TypeProps<DatePickerProps<Dayjs | string>>>(getDatePicker(AntDatePicker));

export const DateTimePicker = compHoc<TypeProps<DatePickerProps<Dayjs | string>>>(
  getDatePicker(AntDatePicker, 'datetime')
);

export const RangePicker = compHoc<TypeProps<RangePickerProps>>(getDatePicker(AntDatePicker.RangePicker, 'range'));

export const TimePicker = compHoc<TypeProps<TimePickerProps>>(getDatePicker(AntTimePicker, '', 'HH:mm:ss'));
